//=============================================================================
///	Hash
//=============================================================================

#include "shash.h"
#include "sstream.h"
#include "smem.h"
#include "sutil.h"
#include "seval.h"		// for push_stack, pop_stack
#include <string.h>

#ifndef	FALSE
#define	FALSE	(0)
#define	TRUE	(1)
#endif




static void hash_mark(SExp s) {
	SHash* hash = &s.ptr->hash;
	HashTable* ht = &hash->ht;

	HashIterator it;
	HashEntry* e;

	// buffer
	smark_ptr(ht->buf);

	for (e = ht_begin(&it, ht); e != NULL; e = ht_next(&it)) {
		// entry work
		smark_ptr(e);

		{	// mark key (SExp)
			SExp s = ptr2s(e->key);
			SMARK(s);
		}

		{	// mark value (SExp)
			SExp s = ptr2s(e->val);
			SMARK(s);
		}
	}
}





static void hash_write(SExp s, SExp strm, int escape) {
	SHash* p = &s.ptr->hash;
	write_cstr("#'{", strm);

	{
		HashIterator it;
		HashEntry* e;
		int first = TRUE;
		for (e = ht_begin(&it, &p->ht); e != NULL; e = ht_next(&it), first = FALSE) {
			if (!first)	write_cstr(",", strm);
			write(ptr2s(e->key), strm, TRUE);
			write_cstr(":", strm);
			write(ptr2s(e->val), strm, TRUE);
		}
	}

	write_cstr("}", strm);
}

static STypeInfo hash_typeinfo = {
	tHash,	0,
	"table",
	NULL,
	NULL,
	hash_mark,
	hash_write,
	NULL,
	NULL,
};


static unsigned int shash(SExp s) {
	switch (type_of(s)) {
	default:		error("can't use hash key: ~a", s);	return 0;
	case tFixnum:	return s2fixnum(s);
	case tSymbol:	return strhash(s.ptr->symbol.str);
	case tString:	return strhash(s.ptr->string.str);
	}
}

static void mark_ht(void* p, void* key, void* item) {
	assert(FALSE);
	smark_ptr(p);
	if (key != NULL) {
		SExp s = ptr2s(key);
		SMARK(s);
	}
	if (item != NULL) {
		SExp s = ptr2s(item);
		SMARK(s);
	}
}


//=============================================================================

static HashVTbl ht_vtbl = {
	smalloc,
	sfree__,
	(unsigned int (*)(const void*))shash,
	(int (*)(const void*, const void*))equal,
};

SExp make_hash_table(void) {
	SHash* p = smalloc(sizeof(*p));
	SExp s = ptr2s(p);
	push_stack(s);
	p->typeinfo= &hash_typeinfo;
	ht_init(&p->ht, &ht_vtbl);
	pop_stack(1);
	return s;
}

SExp gethash(SExp key, SHash* p) {
	HashEntry* e = ht_find(&p->ht, key.ptr);
	if (e != NULL) {
		return ptr2s(e->val);
	} else {
		return sUNDEF;
	}
}

void sethash(SExp key, SHash* p, SExp val) {
	HashEntry* e = ht_find(&p->ht, key.ptr);
	if (e != NULL) {
		e->val = val.ptr;
	} else {
			// expand?
		if (p->ht.bufsiz == 0) {
			const int SIZ = 255;
			ht_resize(&p->ht, SIZ);
		}

		ht_add_direct(&p->ht, key.ptr, val.ptr);
	}
}

